package com.shape.sdk;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.LongSparseArray;

import com.shape.sdk.collect.DeviceData;
import com.shape.sdk.collect.ICollector;
import com.shape.sdk.net.CourserUserResponse;
import com.shape.sdk.net.ResultRequest;
import com.shape.sdk.net.ShapeContants;
import com.shape.sdk.net.ShapeHttpClient;
import com.wandoujia.gson.Gson;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by ouzhiyin on 18/01/2018.
 */

public class Fitness {
  public enum State {
    INIT,
    READY,
    PREPARE,
    RUNNING,
    PAUSE,
    STOP
  }

  private Context context;
  private int station; // 从 1 开始
  private int stationCount;
  private int nextStep;
  private Course course;
  private Reporter reporter;
  private List<CourseUser> allUsers;
  private ICollector collector;
  private LongSparseArray<DeviceData> currentDevicesData;
  private LongSparseArray<List<DeviceData>> countingData; // 5 秒采集一次，一分钟计算一次
  private LongSparseArray<List<InnerFitnessData>> countedData;
  private Handler handler;
  private State state;
  private int countingTick;

  public Fitness(Context context,
                 int station,
                 int stationCount,
                 Reporter reporter,
                 Course course,
                 ICollector collector) {
    this.context = context;
    this.station = station;
    this.stationCount = stationCount;
    this.reporter = reporter;
    this.collector = collector;
    this.course = course;
    state = State.READY;
    countingTick = 0;
    nextStep = this.station - 1;
    currentDevicesData = new LongSparseArray<>();
    countingData = new LongSparseArray<>();
    countedData = new LongSparseArray<>();
    handler = new Handler(Looper.getMainLooper());
    collector.start();
    updateUsers();
    doTick();
  }

  public List<FitnessData> getUsersFitness() {
    List<FitnessData> result = new ArrayList<>();
    if (allUsers == null || station > stationCount) {
      return result;
    }
    int count = 0;
    for (CourseUser courseUser : allUsers) {
      if (count % stationCount == nextStep) {
        FitnessData fitnessData = new FitnessData();
        fitnessData.user = courseUser.clone();
        if (state == State.RUNNING || state == State.STOP) {
          List<InnerFitnessData> fitnessDataList = countedData.get(courseUser.getUserId());
          if (fitnessDataList != null) {
            for (InnerFitnessData innerFitnessData : fitnessDataList) {
              fitnessData.calories += innerFitnessData.calories;
            }
          }
        }
        DeviceData deviceData = currentDevicesData.get(courseUser.getUserId());
        if (deviceData != null) {
          fitnessData.heartRate = deviceData.hr;
          fitnessData.percent = getPercent(deviceData.hr, courseUser);
        }
        result.add(fitnessData);
      }
      count++;
    }
    return result;
  }

  protected List<FitnessData> getRealHeartRate() {
    List<FitnessData> result = new ArrayList<>();
    if (allUsers == null || station > stationCount) {
      return result;
    }
    int count = 0;
    for (CourseUser courseUser : allUsers) {
      if (count % stationCount == nextStep) {
        FitnessData fitnessData = new FitnessData();
        fitnessData.user = courseUser.clone();
        DeviceData deviceData = currentDevicesData.get(courseUser.getUserId());
        if (deviceData != null) {
          fitnessData.heartRate = deviceData.hr;
          fitnessData.percent = getPercent(deviceData.hr, courseUser);
        }
        result.add(fitnessData);
      }
      count++;
    }
    return result;
  }

  public List<FitnessData> getResultReport() {
    List<FitnessData> result = new ArrayList<>();
    if (allUsers == null) {
      return result;
    }
    int count = 0;
    for (CourseUser courseUser : allUsers) {
      if (count % stationCount == nextStep) {
        FitnessData fitnessData = new FitnessData();
        fitnessData.user = courseUser.clone();
        List<InnerFitnessData> innerFitnessDatas = countedData.get(courseUser.getUserId());
        if (innerFitnessDatas != null && innerFitnessDatas.size() != 0) {
          for (InnerFitnessData innerFitnessData : innerFitnessDatas) {
            fitnessData.heartRate += innerFitnessData.heartRate;
            fitnessData.calories += innerFitnessData.calories;
          }
          fitnessData.heartRate = fitnessData.heartRate / innerFitnessDatas.size();
          fitnessData.percent = getPercent(fitnessData.heartRate, courseUser);
        }
        result.add(fitnessData);
      }
      count++;
    }
    return result;
  }

  public Course getCourse() {
    return course;
  }

  public State getState() {
    return state;
  }

  public void prepare() {
    state = State.PREPARE;
  }

  public void start() {
    Log.d("shapeInner", "fitness start");
    state = State.RUNNING;
  }

  public void next() {
    Log.d("shapeInner", "fitness next");
    nextStep++;
    if (nextStep == stationCount) {
      nextStep = 0;
    }
  }

  public void pause() {
    state = State.PAUSE;
  }

  public void resume() {
    state = State.RUNNING;
  }

  public void stop() {
    Log.d("shapeInner", "fitness stop");
    state = State.STOP;
    collector.stop();
    // 非站位盒子不用上报
    if (station > stationCount) {
      return;
    }
    reporter.report(this);
  }

  private void doTick() {
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        onTick();
      }
    }, 1000);
  }

  private void onTick() {
    if (state == State.STOP) {
      return;
    }
    // TODO 更新报名用户
    // TODO 获取心跳
    if ((state == State.READY || state == State.RUNNING || state == State.PREPARE) && allUsers != null) {
      if (countingTick % 5 == 0) {
        updateDeviceData();
      }
      //TODO 每分钟计算心跳
      if (state == State.RUNNING) {
        if (countingTick++ == 60) {
          countingTick = 0;
          countingDeviceData();
        }
      }
    }
    doTick();
  }

  private void updateDeviceData() {
    if (station > stationCount) {
      return;
    }
    for (CourseUser courseUser : allUsers) {
      DeviceData deviceData = collector.getHeartRate(courseUser.deviceId);
      if (deviceData == null) {
        continue;
      }
      currentDevicesData.append(courseUser.getUserId(), deviceData);
      if (state == State.RUNNING) {
        List<DeviceData> deviceDataList = countingData.get(courseUser.getUserId(), null);
        if (deviceDataList == null) {
          deviceDataList = new ArrayList<>();
        }
        deviceDataList.add(deviceData);
        countingData.append(courseUser.getUserId(), deviceDataList);
      }
    }
  }

  private void countingDeviceData() {
    if (station > stationCount) {
      return;
    }
    for (CourseUser courseUser : allUsers) {
      List<DeviceData> deviceDataList = countingData.get(courseUser.getUserId(), null);
      InnerFitnessData innerFitnessData = new InnerFitnessData();
      if (deviceDataList == null || deviceDataList.size() ==0) {
        continue;
      }
      long totalHeartRate = 0;
      for (DeviceData deviceData : deviceDataList) {
        totalHeartRate += deviceData.hr;
      }
      innerFitnessData.heartRate = (int) (totalHeartRate / deviceDataList.size());
      innerFitnessData.calories = getMinutesCalorie(courseUser, innerFitnessData.heartRate);
      innerFitnessData.percent = innerFitnessData.heartRate * 100 / getMaxHeatRate(courseUser);
      if (innerFitnessData.percent > 100) {
        innerFitnessData.percent = 99;
      }

      List<InnerFitnessData> useInnerFitnessDatalist = countedData.get(courseUser.getUserId(), null);
      if (useInnerFitnessDatalist == null) {
        useInnerFitnessDatalist = new ArrayList<>();
      }
      useInnerFitnessDatalist.add(innerFitnessData);
      countedData.append(courseUser.getUserId(), useInnerFitnessDatalist);
    }
  }

  String getResultReportJson() {
    ResultRequest resultRequest = new ResultRequest();
    if (allUsers == null) {
      return "";
    }
    int count = 0;
    UserResultRecord userRecord = new UserResultRecord();
    FitnessData avgFitness = new FitnessData();
    Gson gson = new Gson();
    for (CourseUser courseUser : allUsers) {
      if (count % stationCount == nextStep) {
        List<InnerFitnessData> innerFitnessDatas = countedData.get(courseUser.getUserId());
        if (innerFitnessDatas != null && innerFitnessDatas.size() != 0) {
          for (InnerFitnessData innerFitnessData : innerFitnessDatas) {
            avgFitness.heartRate += innerFitnessData.heartRate;
            avgFitness.calories += innerFitnessData.calories;
          }
          avgFitness.heartRate = avgFitness.heartRate / innerFitnessDatas.size();
          avgFitness.percent = getPercent(avgFitness.heartRate, courseUser);
        } else {
          avgFitness.heartRate = 0;
          avgFitness.calories = 0;
          avgFitness.percent = 0;
        }
        userRecord.avgFitness = avgFitness;
        userRecord.course = course;
        userRecord.user = courseUser;
        userRecord.fitnessDatas = innerFitnessDatas;
        resultRequest.addRecord(courseUser.getUserId(), course.getScheduleId(), gson.toJson(userRecord));
      }
      count++;
    }
    return gson.toJson(resultRequest);
  }


  private int getMaxHeatRate(CourseUser user) {
    return (int) (208 - 0.7*user.age);
  }

  private int getMinHeatRate(CourseUser user) {
    return (int) ((208 - 0.7*user.age) * 0.4);
  }

  private int getPercent(int heartRate, CourseUser user) {
    int result = heartRate * 100 / getMaxHeatRate(user);
    if (result >= 100) {
      result = 99;
    }
    return result;
  }

  private int getMinutesCalorie(CourseUser user, int heartRate) {
    int maxHr = getMaxHeatRate(user);
    int minHr = getMinHeatRate(user);
    float f1 = (float)(3.5D * user.getWeight() / 200000.0D) * 1000;
    float f2 = (float)(100.0D * heartRate / maxHr);
    if (f2 < 40.0F) {
      return (int)(f1);
    }
    if (f2 < 50.0F) {
      return (int)(1.5 * f1);
    }
    if (f2 < 60.0F) {
      return (int)(f1 * 2.0);
    }
    return (int)(f1 * (float)(1.0D + 9.0D * (heartRate - minHr) / (maxHr - minHr)));
  }

  private void fakeQueryUsers() {
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        allUsers = FakeDataManager.getCourseUser(course.getScheduleId());
        updateUsers();
        state = State.READY;
      }
    }, 5000);
  }

  void updateUsers() {
    if (state == State.RUNNING) {
      return;
    }
    final Map<String, String> map = new HashMap<>();
    map.put("schedule_id", String.valueOf(course.getScheduleId()));
    ShapeHttpClient<CourserUserResponse> client = new ShapeHttpClient<>(CourserUserResponse.class);
    client.requestGetAsync(ShapeContants.QUERY_COURSE_SIGNED_USER, map,
        new ShapeHttpClient.Listener<CourserUserResponse>() {
      @Override
      public void onSuccess(CourserUserResponse response) {
        if (FakeDataManager.getWeekCourse() != null) {
          allUsers = FakeDataManager.getCourseUser(course.getScheduleId());
        } else {
          allUsers = response.getUserList();
        }
      }

      @Override
      public void onError(int errorCode, String msg) {

      }
    });
  }


}
